package com.tyjc.prizeOptimize;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Optimize {


	/**
	 *
	 * @param matchSelects 选择的场次内容
	 * @param betNum 投注注数
	 * @param optimizeType 优化类型 平均 搏热 搏冷
	 * @param splitNum 拆分场次个数   2串1为2  3串1为3
	 */
	public List<OptimizedMatch> optimizeMethod(List<MatchSelect> matchSelects, int betNum, OptimizeType optimizeType, int[] splitNum) throws OptimizeException {


		if(splitNum.length>1&&optimizeType!=OptimizeType.AVE) {
			throw new OptimizeException("搏冷搏热仅支持n场n串1");
		}
		for(int split:splitNum) {
			if(matchSelects.size()>split&&optimizeType!=OptimizeType.AVE) {
				throw new OptimizeException("搏冷搏热仅支持n场n串1");
			}
		}
//		if(betNum<matchSelects.size()) {
//			LogUtil.e("betNum>"+betNum+"<matchSelects.size]["+matchSelects.size());
//			throw new OptimizeException("Optimize betNum error");
//		}

		List<OptimizedMatch> standardizeSelects = new ArrayList<OptimizedMatch>();
		for(int split:splitNum) {
			standardizeSelects.addAll(standardizeSelects(matchSelects, split));
		}
		Collections.sort(standardizeSelects);
		if(optimizeType==OptimizeType.AVE) {
			standardizeSelects = optimizeAve(standardizeSelects, betNum);
		}else if(optimizeType==OptimizeType.COLD) {
			standardizeSelects = optimizeCold(standardizeSelects, betNum);
		}else if(optimizeType==OptimizeType.HOT) {
			standardizeSelects = optimizeHot(standardizeSelects, betNum);
		}
		return standardizeSelects;
	}



	/**
	 * 平均优化
	 * @param standardizeSelects
	 * @return
	 */
	private List<OptimizedMatch> optimizeAve(List<OptimizedMatch> standardizeSelects,int betNum) {
		BigDecimal[] rs = optimizeAveMultiple(standardizeSelects, betNum);
		int sum = getSum(rs).intValue();

		//相等直接返回
		if(sum==betNum) {

		} else {//不相等用每个加减1，取标准差最小的
			int minus = Math.abs(sum - betNum);
			int symbol = sum>betNum?-1:1;


			while(minus!=0) {
				double minDeviation = Double.MAX_VALUE;
				int postion = -1;
				for(int i=0;i<rs.length;i++) {
					if(rs[i].intValue()+symbol<=0) {
						continue;
					}
					double standardDeviation = getStandardDeviation(getMutilePrize(standardizeSelects, i,symbol,rs));
					if(standardDeviation<minDeviation) {
						minDeviation = standardDeviation;
						postion = i;
					}
				}
				rs[postion] = rs[postion].add(new BigDecimal(symbol));
				minus = minus - 1;
				if(minus==0) {
					break;
				}
			}
		}
		for(int i=0;i<standardizeSelects.size();i++) {
			standardizeSelects.get(i).setMultiple(rs[i].intValue());
			standardizeSelects.get(i).setTotalPrize(standardizeSelects.get(i).getSinglePrize().multiply(rs[i]));
		}
		return standardizeSelects;
	}


	private BigDecimal[] optimizeAveMultiple(List<OptimizedMatch> standardizeSelects,int betNum) {
		//总奖金
		BigDecimal prizeTotal = BigDecimal.ZERO;
		for(OptimizedMatch match:standardizeSelects) {
			prizeTotal = prizeTotal.add(match.getSinglePrize());
		}

		//标准化
		BigDecimal multipleTotal = BigDecimal.ZERO;
		BigDecimal[] multiples = new BigDecimal[standardizeSelects.size()];
		for(int i=0;i<standardizeSelects.size();i++) {
			multiples[i] = prizeTotal.divide(standardizeSelects.get(i).getSinglePrize(),3,RoundingMode.HALF_UP);
			multipleTotal = multipleTotal.add(multiples[i]);
		}

		for(int i=0;i<multiples.length;i++) {
			multiples[i] = multiples[i].multiply(new BigDecimal(betNum)).divide(multipleTotal,0,RoundingMode.FLOOR);
			if(multiples[i] == BigDecimal.ZERO) {
				multiples[i] = BigDecimal.ONE;
			}
		}

		return multiples;
	}


	private BigDecimal[] getMutilePrize(List<OptimizedMatch> standardizeSelects,int i,int symbol,BigDecimal[] multiples) {
		BigDecimal[] prize = new BigDecimal[standardizeSelects.size()];
		for(int j=0;j<standardizeSelects.size();j++) {
			if(i==j) {
				prize[j] = standardizeSelects.get(j).getSinglePrize().multiply(multiples[j].add(new BigDecimal(1*symbol)));
			}else {
				prize[j] = standardizeSelects.get(j).getSinglePrize().multiply(multiples[j]);
			}
		}
		return prize;
	}



	private double getStandardDeviation(BigDecimal[] prize) {
		return new StandardDeviation(prize).calStandardDeviation();
	}


	private BigDecimal getSum(BigDecimal[] samples) {
		BigDecimal total = BigDecimal.ZERO;
		for(BigDecimal sample:samples) {
			total = total.add(sample);
		}
		return total;
	}


	/**
	 * 搏热
	 * @param standardizeSelects
	 * @param betNum
	 * @return
	 */
	private List<OptimizedMatch> optimizeHot(List<OptimizedMatch> standardizeSelects,int betNum) {
		BigDecimal[] multiples = new BigDecimal[standardizeSelects.size()];
		for(int i=0;i<standardizeSelects.size();i++) {
			multiples[i] = new BigDecimal(betNum*2).divide(standardizeSelects.get(i).getSinglePrize(),0,RoundingMode.UP);
			if(multiples[i].intValue()==0) {
				multiples[i] = new BigDecimal(1);
			}
		}

		int sum = getSum(multiples).intValue();

		int multipleCold = 0;

		if(sum<betNum) {
			multipleCold = betNum - sum;
			multiples[0] = multiples[0].add(new BigDecimal(multipleCold));
		}else {
			int minus = Math.abs(sum - betNum);
			while(minus!=0) {
				for(int i=0;i<multiples.length;i++) {
					if(multiples[i].intValue()>1) {
						multiples[i] = multiples[i].subtract(BigDecimal.ONE);
						minus = minus - 1;
						if(minus == 0) {
							break;
						}
					}
				}
			}
		}
		for(int i=0;i<standardizeSelects.size();i++) {
			standardizeSelects.get(i).setMultiple(multiples[i].intValue());
			standardizeSelects.get(i).setTotalPrize(standardizeSelects.get(i).getSinglePrize().multiply(multiples[i]));
		}
		return standardizeSelects;
	}

	/**
	 * 搏冷
	 * @param standardizeSelects
	 * @param betNum
	 * @return
	 */
	private List<OptimizedMatch> optimizeCold(List<OptimizedMatch> standardizeSelects,int betNum) {
		BigDecimal[] multiples = new BigDecimal[standardizeSelects.size()];
		for(int i=0;i<standardizeSelects.size();i++) {
			multiples[i] = new BigDecimal(betNum*2).divide(standardizeSelects.get(i).getSinglePrize(),0,RoundingMode.UP);
			if(multiples[i].intValue()==0) {
				multiples[i] = new BigDecimal(1);
			}
		}

		int sum = getSum(multiples).intValue();

		int multipleCold = 0;

		if(sum<betNum) {
			multipleCold = betNum - sum;
			multiples[multiples.length-1] = multiples[multiples.length-1].add(new BigDecimal(multipleCold));
		}else {
			int minus = Math.abs(sum - betNum);
			while(minus!=0) {
				for(int i=0;i<multiples.length;i++) {
					if(multiples[i].intValue()>1) {
						multiples[i] = multiples[i].subtract(BigDecimal.ONE);
						minus = minus - 1;
						if(minus == 0) {
							break;
						}
					}
				}
			}
		}
		for(int i=0;i<standardizeSelects.size();i++) {
			standardizeSelects.get(i).setMultiple(multiples[i].intValue());
			standardizeSelects.get(i).setTotalPrize(standardizeSelects.get(i).getSinglePrize().multiply(multiples[i]));
		}
		return standardizeSelects;
	}


	public BigDecimal[][] generateMatrix(List<OptimizedMatch> standardizeSelects,int betNum) {
		int matrixLength = standardizeSelects.size();
		BigDecimal[][] matrix = initMatrix(matrixLength);
		matrix[0][matrixLength] = new BigDecimal(betNum);
		for(int i=0;i<matrixLength;i++) {
			matrix[0][i] = BigDecimal.ONE;
			if(i==0) {
				matrix[1][0] = standardizeSelects.get(i).getSinglePrize();
			}else if(i==matrixLength-1) {
				matrix[matrixLength-1][matrixLength-1] = standardizeSelects.get(i).getSinglePrize().multiply(new BigDecimal(-1));
			}else {
				matrix[i][i] = standardizeSelects.get(i).getSinglePrize().multiply(new BigDecimal(-1));
				matrix[i+1][i] = standardizeSelects.get(i).getSinglePrize();
			}
		}
		return matrix;
	}


	private BigDecimal[][] initMatrix(int matrixLength) {
		BigDecimal[][] matrix = new BigDecimal[matrixLength][matrixLength+1];
		for(int i=0;i<matrixLength;i++) {
			for(int j=0;j<matrixLength+1;j++) {
				matrix[i][j] = BigDecimal.ZERO;
			}
		}
		return matrix;
	}


	private List<OptimizedMatch> standardizeSelects(List<MatchSelect> matchSelects, int splitNum) {
		List<List<MatchSelect>> matches = getCodeCollectionE(matchSelects, splitNum);
		return convertToStandardMatch(matches);
	}


	private List<OptimizedMatch> convertToStandardMatch(List<List<MatchSelect>> matches) {

		List<OptimizedMatch> optimizedMatchs = new ArrayList<OptimizedMatch>();

		List<List<ItemSelect>> items = new ArrayList<List<ItemSelect>>();
		for(List<MatchSelect> matchSelects:matches) {
			List<List<ItemSelect>> temporary = new ArrayList<List<ItemSelect>>();
			for(MatchSelect matchSelect:matchSelects) {
				temporary = rezuheList(temporary, matchSelect.getItemSelects());
			}
			items.addAll(temporary);
		}

		for(List<ItemSelect> item:items) {
			OptimizedMatch match = new OptimizedMatch();
			match.setItemSelects((ArrayList)item);
			match.setMultiple(1);
			match.setSinglePrize(getPrize(item));
			match.setTotalPrize(match.getSinglePrize().multiply(new BigDecimal(match.getMultiple())));
			optimizedMatchs.add(match);
		}
		return optimizedMatchs;
	}



	private BigDecimal getPrize(List<ItemSelect> items) {
		BigDecimal prize = new BigDecimal("2");
		for(ItemSelect item:items) {
			prize = prize.multiply(item.getSpvalue());
		}
		prize.setScale(2,RoundingMode.HALF_UP);
		return prize;
	}

	/**
	 * 自上而下的组合方式如a,bc,d，组合为abd，acd
	 * @param listlistOri 原始组合列
	 * @param as 新添加组合列
	 * @return
	 */
	private List<List<ItemSelect>> rezuheList(List<List<ItemSelect>> listlistOri,List<ItemSelect> as) {
		List<List<ItemSelect>> listlist = new ArrayList<List<ItemSelect>>();
		if(listlistOri.size()==0) {
			for(ItemSelect a:as) {
				List<ItemSelect> list = new ArrayList<ItemSelect>();
				list.add(a);
				listlist.add(list);
			}
			return listlist;
		}

		for(List<ItemSelect> list:listlistOri) {
			for(ItemSelect a:as) {
				ArrayList<ItemSelect> newlist = new ArrayList<ItemSelect>();
				newlist.addAll(list);
				newlist.add(a);
				listlist.add(newlist);
			}
		}
		return listlist;
	}


	private <T> List<List<T>> getCodeCollectionE(List<T> betcodes, int select) {
		int[] a = new int[betcodes.size()];
		for (int i = 0; i < a.length; i++) {
			a[i] = i;
		}
		int[] b = new int[select];
		List<int[]> list = new ArrayList<int[]>();
		combine(a, a.length, select, b, select, list);
		List<List<T>> reList = new ArrayList<List<T>>();
		for (int[] result : list) {
			List<T> codeList = new ArrayList<T>();
			for (int p : result) {
				codeList.add(betcodes.get(p));
			}
			reList.add(codeList);
		}
		return reList;
	}


	private void combine(int a[], int n, int m, int b[], final int M, List<int[]> list) {
		for (int i = n; i >= m; i--) {
			b[m - 1] = i - 1;
			if (m > 1)
				combine(a, i - 1, m - 1, b, M, list);
			else {
				int[] result = new int[M];
				for (int j = M - 1; j >= 0; j--) {
					result[j] = a[b[j]];
				}
				list.add(result);
			}
		}
	}
}